package com.oswift.utils.file;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.oswift.utils.common.StreamUtil;
import com.oswift.utils.exception.PlatException;

/**
 *
 * 文件操作工具类
 *
 * @author zhuou
 * @version C03 Oct 27, 2012
 * @since OSwift GPM V1.0
 */
public final class FileUtil
{
    /**
     * 文件路径分隔符
     */
    public static final String FILE_SEPARATOR = System
            .getProperty("file.separator");

    /**
     * 路径分隔符
     */
    public static final String SLASH = "/";

    /**
     * 日志对象
     */
    private static Logger log = LoggerFactory.getLogger(FileUtil.class);

    private FileUtil()
    {
    }

    /**
     * 复制整个文件夹内容
     *
     * @param oldPath
     *            String 原文件路径 如：c:/fqf
     * @param newPath
     *            String 复制后路径 如：f:/fqf/ff
     * @throws GPMException
     *             通用异常
     */
    public static void copyFolder(String oldPath, String newPath)
        throws PlatException
    {

        try
        {
            // 如果文件夹不存在 则建立新文件夹
            File dest = new File(newPath);

            if ((!dest.exists()) && (!dest.mkdirs()))
            {
                log.warn(String.format("Create dir = %s failed!", newPath));
            }

            File a = new File(oldPath);
            String[] file = a.list();
            File temp = null;

            for (int i = 0; i < file.length; i++)
            {
                if (oldPath.endsWith(File.separator))
                {
                    temp = new File(oldPath + file[i]);
                }
                else
                {
                    temp = new File(oldPath + File.separatorChar + file[i]);
                }

                if (temp.isFile())
                {

                    copyFile(temp.getAbsolutePath(), newPath
                            + File.separatorChar + temp.getName());

                }
                if (temp.isDirectory())
                {
                    // 如果是子文件夹
                    copyFolder(oldPath + File.separatorChar + file[i], newPath
                            + File.separatorChar + file[i]);
                }
            }
        }
        catch (Exception e)
        {
            throw new PlatException("Copy files error!", e);
        }
    }

    /**
     * 复制单个文件
     *
     * @param oldPath
     *            String 原文件路径 如：c:/fqf.txt
     * @param newPath
     *            String 复制后路径 如：f:/fqf.txt
     */
    public static void copyFile(String oldPath, String newPath)
    {
        // 创建文件对象
        File oldfile = new File(oldPath);
        if (oldfile.exists())
        {
            FileInputStream fis = null;
            FileOutputStream fos = null;

            try
            {
                fis = new FileInputStream(oldPath);
                fos = new FileOutputStream(newPath);

                // 创建缓冲区
                final int bufferInitSize = 1444;
                byte[] buffer = new byte[bufferInitSize];

                int bytesum = 0;
                int byteread = 0;

                // 读取源文件内容
                while ((byteread = fis.read(buffer)) != -1)
                {
                    // 记录读取的字节总数
                    bytesum += byteread;

                    // 写入目标文件
                    fos.write(buffer, 0, byteread);
                }
            }
            catch (Exception e)
            {
                log.error("Fail to copy file, the src file is " + oldPath
                        + ", the new file is " + newPath, e);
            }
            finally
            {
                StreamUtil.getInstance().close(fis, fos);
            }
        }
        else
        {
            log.warn("The file to be copyed is not exist, the file is "
                    + oldPath);
        }
    }

    /**
     * 删除文件或者文件夹,对于文件夹遍历其子文件夹进行递归删除
     *
     * @param file
     *            要删除的文件或文件夹
     * @return 删除是否成功: true - 删除成功; false - 删除失败.
     */
    public static boolean deleteFile(File file)
    {
        if (!file.exists())
        {
            log.warn("The file to be deleted is not exist, the file is "
                    + file.getAbsoluteFile());
            return true;
        }

        // 是文件,则直接删除
        if (file.isFile())
        {
            return file.delete();
        }

        // 是目录,则递归删除目录下所有文件
        if (file.isDirectory())
        {
            // 获取目录下的所有文件
            File[] files = file.listFiles();

            // 遍历文件
            for (File f : files)
            {
                // 删除文件
                if (!deleteFile(f))
                {
                    log.warn("Fail to delete file:" + file.getAbsolutePath());
                    return false;
                }
            }

            // 删除目录
            return file.delete();
        }
        else
        {
            return false;
        }
    }

    /**
     * 向文件中附加内容
     *
     * @param fileName
     *            文件路径
     * @param bytes
     *            要写入文件的内容
     * @return 是否成功
     */
    public static boolean appendFile(String fileName, byte[] bytes)
    {
        // 设置添加成功标记
        boolean success = true;

        // 声明文件通道
        FileChannel channel = null;
        try
        {
            // 采用追加形式，写入文件内容
            boolean isAppend = true;

            // 通过文件输入流获取文件通道
            channel = new FileOutputStream(fileName, isAppend).getChannel();
            channel.write(ByteBuffer.wrap(bytes));
        }
        catch (IOException e)
        {
            log.error("Fail to append write data to file:" + fileName, e);
            success = false;
        }
        finally
        {
            StreamUtil.getInstance().close(channel);
        }
        return success;
    }

    /**
     * 创建指定名称的临时文件
     *
     * @param prefix
     *            指定的临时文件名称
     * @param content
     *            文件内容
     * @param dir
     *            文件路径
     * @return 文件对象
     */
    public static File createTempFile(String prefix, byte[] content, File dir)
    {
        File temp = null;
        if (null == content)
        {
            return temp;
        }

        FileOutputStream fos = null;
        try
        {
            // 创建临时文件，且不指定后缀，采用默认后缀名.tmp
            temp = File.createTempFile(prefix, null, dir);
            fos = new FileOutputStream(temp);
            fos.write(content);
            fos.flush();
        }
        catch (IOException e)
        {
            log.error("Fail to write data to a temp file:"
                    + dir.getAbsolutePath() + prefix + ".tmp", e);
        }
        finally
        {
            if (fos != null)
            {
                try
                {
                    fos.close();
                }
                catch (IOException e)
                {
                    // 忽略异常
                    log.warn("Close fileoutputstream failed.");
                }
            }
        }

        return temp;
    }

    /**
     * 获取文文件全路径
     *
     * @param filename
     *            文件名
     * @return 文件全路径，不存在时返回null
     */
    public static String getPathForFileName(String filename)
    {

        File configFile = new File(filename);

        // 不是绝对路径
        if (!configFile.exists())
        {
            if (!filename.startsWith(SLASH))
            {
                filename = SLASH + filename;
            }

            URL url = FileUtil.class.getResource(filename);

            // 文件不存在时返回null
            if (null == url)
            {
                log.error("The config File[filePath=" + filename
                        + "] does not exist.");
                return null;
            }

            // 创建文件实例

            try
            {
                configFile = new File(url.toURI());
            }
            catch (URISyntaxException e)
            {
                log.error("The config File[filePath=" + filename
                        + "] does not exist.", e);
                return null;
            }

        }
        // 返回文件所在的路径
        return configFile.toString();
    }

    /**
     * 读取指定路径下的指定文件
     *
     * @param path
     *            文件路径
     * @return 文件内容
     */
    public static String readEntireFile(String path)
    {

        if (null == path)
        {
            log.warn("the file path is null.");
            return null;
        }

        File file = null;
        FileReader fr = null;
        BufferedReader br = null;
        StringBuffer fileString = new StringBuffer();

        try
        {
            file = new File(path);
            fr = new FileReader(file);
            br = new BufferedReader(fr);
            String str = null;
            while ((str = br.readLine()) != null)
            {
                fileString.append(str);
            }
        }
        catch (Exception e)
        {
            log.error("Fail to read file, the src file is " + path + ".", e);
        }
        finally
        {
            StreamUtil.getInstance().close(fr, br);
        }
        return fileString.toString();
    }

    /**
     * 使用快速排序法，按照字典顺序根据文件名为文件排序
     *
     * @author zhuou
     * @param files
     *            文件数组
     * @param left
     *            左边下标
     * @param right
     *            右边下标
     */
    public static void sortFilesByName(File[] files, int left, int right)
    {

        int i, j;
        String middle;
        File tmp;

        i = left;
        j = right;

        middle = files[left].getName();
        do
        {
            while ((files[i].getName().compareToIgnoreCase(middle) < 0)
                    && (i < right))
            {
                i++;
            }
            while ((files[j].getName().compareToIgnoreCase(middle) > 0)
                    && (j > left))
            {
                j--;
            }
            if (i <= j)
            {
                tmp = files[i];
                files[i] = files[j];
                files[j] = tmp;
                i++;
                j--;
            }
        }
        while (i <= j);

        if (left < j)
        {
            sortFilesByName(files, left, j);
        }
        if (right > i)
        {
            sortFilesByName(files, i, right);
        }
    }

    /**
     * 新建文件夹，如果父文件夹不存在，遍历新建<br>
     * 如果同名文件夹已经存在，则不做操作，返回true<br>
     * 如果同名非文件夹文件存在，则删除，并创建文件夹<br>
     * 如果同名文件夹不存在，则创建文件夹
     *
     * @author zhuou
     * @param filePath
     *            文件夹路径
     * @return true成功；false失败
     */
    public static boolean createDirectory(String filePath)
    {
        // 参数校验
        if (null == filePath || filePath.length() == 0)
        {
            return false;
        }

        File file = new File(filePath);

        // 判空
        if (null == file)
        {
            return false;
        }

        // 标识变量
        boolean flag = true;

        // 同名文件存在
        if (file.exists())
        {
            // 同名文件不是文件夹
            if (!file.isDirectory())
            {
                // 删除
                flag = deleteFile(file);

                // 如果删除成功，再创建文件夹
                if (flag)
                {
                    flag = file.mkdirs();
                }
            }
        }
        // 同名文件不存在
        else
        {
            flag = file.mkdirs();
        }

        return flag;
    }

    /**
     * 在文件路径后面添加文件间隔符<br>
     * 如果path为空，则返回null
     *
     * @author zhuou
     * @param path
     *            待处理的文件路径
     * @return 处理后的文件路径
     */
    public static String appendSeprator(String path)
    {
        if (null == path || path.length() == 0)
        {
            return null;
        }

        return path.endsWith(FILE_SEPARATOR) ? path : path + FILE_SEPARATOR;
    }

    /**
     *
     * 获取文件MD5后字符串
     *
     * @author zhuou
     * @param file
     *            File
     * @return String
     */
    public static String getFileMD5(File file)
    {
        if (!file.isFile())
        {
            return null;
        }
        MessageDigest digest = null;
        FileInputStream in = null;
        byte buffer[] = new byte[1024];
        int len;
        try
        {
            digest = MessageDigest.getInstance("MD5");
            in = new FileInputStream(file);
            while ((len = in.read(buffer, 0, 1024)) != -1)
            {
                digest.update(buffer, 0, len);
            }
            in.close();
        }
        catch (Exception e)
        {
            return null;
        }
        BigInteger bigInt = new BigInteger(1, digest.digest());
        return bigInt.toString(16);
    }
}
